package com.cn.qtms.service.impl;//类注释设置模板

import com.cn.qtms.base.dao.BaseDao;
import com.cn.qtms.base.service.impl.BaseServiceImpl;
import com.cn.qtms.dao.TmSecKillDao;
import com.cn.qtms.entity.TmSecGoods;
import com.cn.qtms.entity.TmSecKill;
import com.cn.qtms.exception.AppException;
import com.cn.qtms.mq.DelayOrderSender;
import com.cn.qtms.ratelimit.RateLimit;
import com.cn.qtms.service.TmSecGoodsService;
import com.cn.qtms.service.TmSecKillService;
import com.cn.qtms.util.DateUtils;
import com.cn.qtms.vo.SecKillVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Name:TmSecKillServiceImpl
 * @Description:
 * @Author: zhangyang
 * @Date: 2019-12-25 19:23
 **/
@Service
@Slf4j
public class TmSecKillServiceImpl extends BaseServiceImpl<TmSecKill, Long> implements TmSecKillService {
    @Autowired
    private TmSecKillDao dao;
    @Autowired
    private TmSecGoodsService secGoodsService;

    /**
     * redis分布式锁
     */

    @Autowired
    private RedisLockService lockService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    DelayOrderSender sender;

    @Override
    public BaseDao<TmSecKill, Long> getDao() {
        return dao;
    }

    /**
     * 注意 创建库存缓存要在商品列表缓存后 不然可能会出现bug
     */
    @Override
    @Transactional
    public void createSecKills() {
        //获取昨日最后一场的秒杀商品信息
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR_OF_DAY, -24);
        final Date yestDay = cal.getTime();
        final String yest_time = DateUtils.format(yestDay, "yyyy-MM-dd");
        final List<TmSecKill> lastSecKill = dao.findLastSecKill(yest_time);
        if (lastSecKill!=null && lastSecKill.size() > 0) {
            lastSecKill.forEach(item -> {
                redisTemplate.opsForValue().set("secgoods_" + item.getGoodsId() + "_" + 0, item);
                //设置redis的过期时间是1天
                redisTemplate.expire("secgoods_" + item.getGoodsId() + "_" + 0, 24 * 60 * 60, TimeUnit.SECONDS);
            });
        }

        //获取今日所有的秒杀商品 不考虑等级
        final List<TmSecGoods> secGoods = secGoodsService.getSecGoods();
        if (secGoods == null || secGoods.size() == 0) {
            log.error("今日秒杀商品为空");
            return;
        }

        secGoods.forEach(item -> {
            //1.添加到sec-kill表中
            TmSecKill kill = new TmSecKill();
            kill.setActId(item.getActId());
            kill.setGoodsId(item.getGoodsId());
            kill.setSecGoodsId(item.getId());
            kill.setKillDate(new Date());
            kill.setActType(item.getActType());
            kill.setEndCount(item.getEachCount());
            kill.setTotalCount(item.getEachCount());
            kill.setStatus("0");
            dao.save(kill);
            //2.添加一场，就减去相应的库存
            item.setCurrentsCount(item.getCurrentsCount() - item.getEachCount());
            secGoodsService.save(item);
            //将场次库存信息储存到redis中 键命名规则 secgoods_goodsId_actType
            redisTemplate.opsForValue().set("secgoods_" + item.getGoodsId() + "_" + item.getActType(), kill);
            //设置redis的过期时间是1天
            redisTemplate.expire("secgoods_" + item.getGoodsId() + "_" + item.getActType(), 24 * 60 * 60, TimeUnit.SECONDS);
        });
    }

    @Override
    @Transactional
    @RateLimit(limitNum = 100)
    public SecKillVo excuteSecKill(SecKillVo vo) {
        if (vo.getGoodsId() == null || vo.getActType() == 0 || vo.getPhone() == null) {
            throw new AppException("当前商品信息未完善，不能参与秒杀");
        }
        //扣减redis商品库存 使用redis锁 锁住扣减库存逻辑
        final boolean redisLock = lockService.setLock("redisLock", 3000);
        if (redisLock) {
            //获取商品库存信息
            System.out.println("开始加分布式锁");
            String key = "secgoods_" + vo.getGoodsId() + "_" + vo.getActType();
            if (redisTemplate.hasKey(key)) {
                final TmSecKill kill = (TmSecKill) redisTemplate.opsForValue().get(key);
                //判断实时库存
                if (kill.getEndCount() > 0) {
                    //创建一个订单
                    sender.createOrder(vo);
                    //创建延时队列 15分钟取消订单
                    sender.sendDelayMessage(vo);
                    //扣减库存
                    kill.setEndCount(kill.getEndCount() - 1);
                    //减少库存
                    redisTemplate.opsForValue().set(key, kill);
                    //设置锁过期时间1天
                    redisTemplate.expire(key, 24 * 60 * 60, TimeUnit.SECONDS);
                    //释放锁
                    lockService.releaseLock("redisLock", lockService.get("redisLock"));
                    String uuid = UUID.randomUUID().toString();
                    vo.setToken(uuid);
                    return vo;
                } else {
                    //释放锁
                    lockService.releaseLock("redisLock", lockService.get("redisLock"));
                    throw new AppException("当前商品没有库存，不能抢购");
                }
                //扣减库存
            } else {
                //释放锁
                System.out.println("开始释放锁");
                lockService.releaseLock("redisLock", lockService.get("redisLock"));
                throw new AppException("所选商品不存在");
            }
        } else {
            throw new AppException("分布式锁被锁住");
        }
    }
}